parser grammar BaseGrammarLoose;

options {
	tokenVocab = CommonLexer;
}

pattern
	locals[CppString type]:
	'{' properties_ = objectPatternElementArrayList? '}' (
		{this->canBeFollowedByTypeAnnotation()}? typeAnnotation_ = typeAnnotation
	)? {
        $type = "ObjectPattern";
    } # ObjectPattern
	| Ellipsis argument_ = pattern (
		{this->canBeFollowedByTypeAnnotation()}? typeAnnotation_ = typeAnnotation
	)? {
        $type = "RestElement";
    } # RestElement
	| '[' elements_ = arrayPatternElementArrayList? ']' (
		{this->canBeFollowedByTypeAnnotation()}? typeAnnotation_ = typeAnnotation
	)? {
        $type = "ArrayPattern";
    } # ArrayPattern
	| identifier {
        $type = "Identifier";
    } # IdentifierPattern;

assignmentPattern
	locals[CppString type]:
	left_ = pattern assignmentOperator right_ = expression {
        $type = "AssignmentPattern";
    };

initializer: '=' expression_ = expression;

parameterList
	locals[boolean isArrayList ]
	@init {
	    $isArrayList = true;
		this->enterParameter();
}:
	parameter (',' parameter)* {
		this->leaveParameter();
};

parameter: assignmentPattern | pattern;

//TODO Leave for later use
advancedParameterList
	locals[ boolean isArrayList ]
	@init {
	    $isArrayList = true;
}:
	spreadElement
	| advancedParameter (',' advancedParameter)* (
		',' spreadElement
	)?;

//TODO 
requiredParameterList:
	requiredParameter (',' requiredParameter)*;

//TODO 
advancedParameter: requiredParameter | optionalParameter;

//TODO 
optionalParameter: id_ = pattern ( '?' | init_ = initializer);

requiredParameter:
	id_ = pattern typeAnnotation_ = typeAnnotation?;

accessibilityModifier
	locals[ CppString type ]:
	Public { $type = "Public";}
	| Private { $type = "Private" ;}
	| Protected { $type = "Protected" ;};

interfaceExtendsClause: Extends classOrInterfaceTypeList;

classOrInterfaceTypeList
	locals[boolean isArrayList]
	@init {
	    $isArrayList = true;
}: typeName (',' typeName)*;

// A.7 Interface

enumDeclaration:
	Const? Enum id_ = identifier '{' body_ = enumBody? '}';

enumBody: memberList_ = enumMemberList ','?;

enumMemberList: enumMember (',' enumMember)*;

enumMember: propertyName ('=' expression)?;

importAliasDeclaration:
	id_ = identifier '=' namespaceName_ = qualifiedName SemiColon;

program
	locals[CppString type , CppString sourceType ]:
	body_ = sourceArrayList? EOF { 
        $type = "Program"; $sourceType = "module"; 
    };

sourceElement: Export? statement_ = statement;

statement
	locals[ CppString type ]:
	braceBlock {

    } # BlockStatement
	| Import (fromBlock | importAliasDeclaration) {
        $type = "ImportStatement";
    } # ImportStatement
	| Export Default? (fromBlock | statement) {
        $type = "ExportStatement";
    } # ExportStatement
	| SemiColon {
        $type = "EmptyStatement";
    } # EmptyStatement
	| Namespace (identifier | qualifiedName) '{' body_ = statementList? '}' {
        $type = "TSModuleDeclaration";
    } # TSModuleDeclaration
	| If '(' test_ = expressionSequence ')' consequent_ = statement (
		Else alternate = statement
	)? {
        $type = "IfStatement";
    } # IfStatement
	| Do body_ = statement While '(' test_ = expressionSequence ')' eos {
        $type = "DoWhileStatement";
    } # DoWhileStatement
	| While '(' test_ = expressionSequence ')' body_ = statement {
        $type = "WhileStatement";
    } # WhileStatement
	| For '(' init_ = forInitStatement? SemiColon test_ = expressionSequence? SemiColon update_ =
		expressionSequence? ')' body_ = statement {
        $type = "ForStatement";
    } # ForStatement
	| For '(' left_ = baseForXStatement In right_ = expressionSequence ')' body_ = statement {
        $type = "ForInStatement";
    } # ForInStatement
	| For '(' left_ = baseForXStatement Of right_ = expressionSequence ')' body_ = statement {
        $type = "ForOfStatement";
    } # ForOfStatement
	| Continue ({this->notLineTerminator()}? label_ = identifier)? eos {
        $type = "ContinueStatement";
    } # ContinueStatement
	| Break ({this->notLineTerminator()}? label_ = identifier)? eos {
        $type = "BreakStatement";
    } # BreakStatement
	| Return ({this->notLineTerminator()}? argument_ = expression) eos {
		$type = "ReturnStatement";
    } # ReturnStatement
	| Return eos {
        $type = "ReturnStatement";
    } # ReturnStatement
	| With '(' object_ = expressionSequence ')' body_ = statement {
        $type = "WithStatement";
    } # WithStatement
	| label_ = identifier ':' body_ = statement {
        $type = "LabeledStatement";
    } # LabeledStatement
	| Switch '(' discriminant_ = expressionSequence ')' cases_ = switchCaseList {
        $type = "SwitchStatement";
    } # SwitchStatement
	| Throw {this->notLineTerminator()}? argument_ = expressionSequence eos {
        $type = "ThrowStatement";
    } # ThrowStatement
	| Try block_ = braceBlock handler_ = catchProduction? finalizer_ = finallyProduction? {
        $type = "TryStatement";
    } # TryStatement
	| Debugger eos {
        $type = "DebuggerStatement";
    }	# DebuggerStatement
	| declaration													# DeclarationStatement
	| 'type' alias_ = identifier '=' original_ = type_ eos {
        $type = "TSTypeAliasDeclaration";
    } # TSTypeAliasDeclaration
	| enumDeclaration {
        $type = "TSEnumDeclaration";
    } # TSEnumDeclaration
	| {this->notOpenBraceAndNotFunction()}? expression_ = expressionSequence SemiColon? {
        $type = "ExpressionStatement";
    } # ExpressionStatement;

braceBlock
	locals[CppString type ]:
	'{' body_ = statementList? '}' {
        $type = "BlockStatement";
    };

declaration:
	functionDeclaration
	| variableDeclaration eos
	| baseClass;

functionDeclaration
	locals[ CppString type ]
	@init {
    $type = "FunctionDeclaration";
}
	@after {
    this->leaveFunction();
}:
	baseFunction_ = baseFunction (
		( body_ = braceBlock)
		| SemiColon
	)?;

baseFunction
	locals[boolean async_ , boolean generator_  ]
	@init {
    this->enterFunction(); 
}:
	Async? Function_ Multiply? id_ = identifier? '(' params_ = parameterList? ')' (
		':' typeAnnotation_ = functionReturnType
	)? {
        $async_ = nullptr != $ctx->Async();
        $generator_ = nullptr != $ctx->Multiply();
    };

baseClass
	locals[CppString type ]
	@init {
    this->enterClassDeclaration();  
}
	@after {
    this->leaveClassDeclaration();
}:
	Declare? Abstract? Class id_ = identifier? superClass_ = classExtendsClause? implements_ =
		implementsClause? body_ = classBody { 
		$type = dynamic_cast<ExpressionContext*>($ctx->parent) ?  "ClassExpression" : "ClassDeclaration";
    };

statementList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}: statement+;

fromBlock: (
		Multiply
		| multipleImportStatement_ = multipleImportStatement
	) (As id_ = identifier)? From StringLiteral eos;

multipleImportStatement: (identifier ',')? '{' identifier (
		',' identifier
	)* '}';

//NOTE @Add variableDeclaration for compatibility with estree
variableDeclaration
	locals[ CppString type ]
	@init {
    this->enterVariableDeclaration();
}
	@after {
    this->leaveVariableDeclaration();
}:
	kind_ = varModifier declarations_ = variableDeclaratorList ';'? {
        $type = "VariableDeclaration";
    }
	| Let eos;

variableDeclaratorList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}: variableDeclarator (',' variableDeclarator)*;

variableDeclarator
	locals[CppString type]:
	id_ = pattern ('=' init_ = expression)? {
        $type = "VariableDeclarator"; 
    }
	| id_ = pattern '=' keyword {
        $type = "VariableDeclarator";
    };

//NOTE @Add  forInitStatement for general for statement
forInitStatement: variableDeclaration | expression;

baseForXStatement: variableDeclaration | pattern | expression;

varModifier: Var | Let | Const;

switchCaseList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}: '{' caseClauses? ( defaultClause caseClauses?)? '}';

caseClauses: caseClause+;

caseClause
	locals[CppString type ]:
	Case test_ = expressionSequence ':' consequent_ = statementList? {
        $type = "SwitchCase";
    };

defaultClause
	locals[CppString type ]:
	Default ':' consequent_ = statementList? {
        $type = "SwitchCase";
    };

catchProduction
	locals[CppString type ]:
	Catch '(' param_ = identifier ')' body_ = braceBlock {
        $type = "CatchClause";
    };

finallyProduction: Finally braceBlock;

classBody
	locals[  boolean isArrayList]
	@init {
	    $isArrayList = true;
}: '{' classElement* '}';

classExtendsClause: Extends superClass_ = typeName;

implementsClause:
	Implements interfaces_ = classOrInterfaceTypeList;

classElement: methodDefinition | propertyDefinition;

//kind: "constructor" | "method" | "get" | "set";
methodDefinition
	locals[ CppString type , CppString kind , boolean computed , boolean static_ , boolean async_ ]
	@init {
    $type = "MethodDefinition";
 }:
	accessibility_ = accessibilityModifier? Static? Async? key_ = propertyName '(' parameterList_ =
		advancedParameterList? ')' (
		body_ = braceBlock
		| SemiColon
	) {
        // $computed  = $ctx->key_ ? $ctx->key_->computed : false;
        // $kind = "method";
        // $static_ = nullptr != $ctx->Static();
        // $async_ = nullptr != $ctx->Async();
    }
	| accessibility_ = accessibilityModifier? Static? Async? (
		getter_ = getAccessor
		| setter_ = setAccessor
	) {
        // $computed  = false;
        // $kind = nullptr == $ctx->getter_ ? "set" : "get";
        // $static_ = nullptr != $ctx->Static();
        // $async_ = nullptr != $ctx->Async();
    }
	| accessibility_ = accessibilityModifier? Constructor '(' parameterList_ = advancedParameterList
		? ')' body_ = braceBlock {
        $computed  = false;
        $kind = "constructor";
        $static_ = false;
        $async_ = false;
    };

propertyDefinition
	locals[ CppString type , boolean computed , boolean static_ , boolean async_ , boolean readonly_ ]
	@init {
    $type = "PropertyDefinition";
 }:
	accessibility_ = accessibilityModifier? Static? ReadOnly? key_ = propertyName '?'?
		typeAnnotation_ = typeAnnotation? value_ = expression? SemiColon {
        // $computed = $ctx->key_ ? $ctx->key_->computed : false;
        // $static_ = nullptr != $ctx->Static();
        // $readonly_ = nullptr != $ctx->ReadOnly();
    };

generatorMethod:
	'*'? identifier '(' parameterList? ')' '{' functionBody '}';

generatorFunctionDeclaration:
	Function_ '*' identifier? '(' parameterList? ')' '{' functionBody '}';

generatorBlock:
	'{' generatorDefinition (',' generatorDefinition)* ','? '}';

generatorDefinition: '*' iteratorDefinition;

iteratorBlock:
	'{' iteratorDefinition (',' iteratorDefinition)* ','? '}';

iteratorDefinition:
	'[' expression ']' '(' parameterList? ')' '{' functionBody '}';

functionBody: sourceArrayList?;

sourceArrayList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}: sourceElement+;

spreadElement
	locals[CppString type]:
	Ellipsis argument_ = expression {
        $type = "SpreadElement";
    };

//TODO validate if the spreadElement appears in the end
arrayElement: spreadElement | expression;

//NOTE the null element is processed while visited @checkNullElementInArray
arrayElementArrayList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}: Comma* (arrayElement | Comma) (Comma+ arrayElement)* Comma*;

arrayPatternElementArrayList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}:
	Comma* (pattern | assignmentPattern | Comma) (
		Comma+ ( pattern | assignmentPattern)
	)* Comma*;

objectElement: property | spreadElement;

objectElementArrayList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}: objectElement (',' objectElement)* ','?;

//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
propertyName
	locals[ boolean computed ]:
	identifier {
        $computed = false;
    }
	| StringLiteral {
        $computed = true;
    }
	| numericLiteral {
        $computed = true;
    }
	| privateIdentifier {
        $computed = false;
    };

//@estree
propertyKey
	@init {
		this->enterPropertyKey();
	}
	@after {
		this->leavePropertyKey();
	}: expression | privateIdentifier;

property
	locals[CppString type , boolean method ,boolean shorthand ,boolean computed , CppString kind ]
	@init {
        $type = "Property";
}:
	key_ = propertyKey ':' value_ = expression {
        $method = false;
        $shorthand = false;
        $computed = false;
        $kind = "init";
    } # PropertyExpressionAssignment
	| '[' key_ = expression ']' ':' value_ = expression {
        $method = false;
        $shorthand = false;
        $computed = true;
        $kind = "init";
    } # ComputedPropertyExpressionAssignment
	| getAccessor {
        $method = true;
        $shorthand = false;
        $computed = false;
        $kind = "get";

    } # PropertyGetter
	| setAccessor {
        $method = true;
        $shorthand = false;
        $computed = false;
        $kind = "set";

    } # PropertySetter
	| generatorMethod {
        $method = false;
        $shorthand = false;
        $computed = true;
        $kind = "init";

    } # MethodProperty
	| key_ = propertyKey {   //NOTE For PropertyShorthand both key and value are all the same 
        $method = false;
        $shorthand = true;
        $computed = false;
        $kind = "init";

    } # PropertyShorthand;

assignmentProperty
	locals[CppString type , boolean method ,boolean shorthand ,boolean computed , CppString kind ]
	@init {
        $type = "Property";
        $kind = "init";
        $method = false;

}:
	key_ = propertyKey { 
        //NOTE the peroperty value is the same with key in ShorthandAssignmentProperty
        $shorthand = true;
        $computed = false;

    } # ShorthandAssignmentProperty
	| value_ = assignmentPattern {
        //NOTE the peroperty key with assignmentPattern in AssignmentPatternInObject is the lhs of the assignmentPattern 
        $shorthand = false;
        $computed = false;
    } # AssignmentPatternInObject;

objectPatternElement: assignmentProperty | spreadElement;

objectPatternElementArrayList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}: objectPatternElement (',' objectPatternElement)* ','?;

getAccessor:
	getter_ = getter '(' ')' typeAnnotation_ = typeAnnotation? '{' body_ = functionBody '}';

setAccessor:
	setter_ = setter '(' (id_ = identifier | pattern_ = pattern) typeAnnotation_ = typeAnnotation?
		')' '{' body_ = functionBody '}';

argumentArrayList
	locals[ boolean isArrayList]
	@init {
	    $isArrayList = true;
}: argument (',' argument)*;

argument: spreadElement | expression;

expressionSequence
	locals[CppString type ]:
	expression (',' expression)* { 
        $type = "SequenceExpression";
    };

updateOperator
	locals[ CppString type , boolean prefix ]
	@init {
        $type = "UpdateOperator";
        $prefix = false;

}: '++' | '--';

unaryOperator
	locals[ CppString type , boolean prefix ]
	@init {
        $type = "UnaryOperator";
        $prefix = true;

}: '-' | '+' | '!' | '~' | 'typeof' | 'void' | 'delete';

exponentiationOperator: '**';

multiplicativeOperator: ('*' | '/' | '%');

additiveOperator: ( '+' | '-');

//NOTE all the three bitShift operrators have the same priority
bitShiftOperator: ('<<' | '>>' | '>>>');

relationalOperator: ('<' | '>' | '<=' | '>=');

instanceofOperator: Instanceof;

inOperator: In;

equalityOperator: ('==' | '!=' | '===' | '!=');

bitAndOperator: '&';

bitOrOperator: '|';

bitXorOperator: '^';

//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
expression
	locals[ CppString type ]:
	baseFunction_ = baseFunction body_ = braceBlock {
        $type = "FunctionExpression";
        this->leaveFunction();
    } # FunctionExpression
	| Async? params_ = arrowFunctionParameters typeAnnotation_ = functionReturnType? '=>' body_ =
		arrowFunctionBody {
        $type = "ArrowFunctionExpression";
    } # ArrowFunctionExpression
	| baseClass {
	} # ClassExpression
	| '(' expression_ = expressionSequence ')' {
        $type = "ParenthesizedExpression";
	} # ParenthesizedExpression
	| object_ = expression property_ = memberExpressionProperty
	//REVIEW  Add proper debug method
	{
        //NOTE make object_ optional in memberpression following estree
        $type = "MemberExpression";
    } # MemberExpression
	//TODO Need verification or improvement
	| New callee = expression '(' arguments_ = argumentArrayList? ')' {
        $type = "NewExpression";
		this->correctNewExpression($ctx);
	} # NewExpression
	| callee_ = expression OptionalChainingOperator? '(' arguments_ = argumentArrayList? ')' {
        $type = "CallExpression";
    } # CallExpression
	| New callee = expression {
        $type = "NewExpression";
	} # NewExpression
	| argument_ = expression {this->notLineTerminator()}? operator_ = updateOperator {
        $type = "UpdateExpression";
    } # UpdateExpression
	| operator_ = updateOperator argument_ = expression {
        //NOTE Do not use dynamic scope while generating Cpp target before bug in template file used by StringTemplate fixed ,e.g. updateOperator::prefix = true;
        $type = "UpdateExpression";
        this->updateOperatorIsPrefix($operator_.ctx);
    } # UpdateExpression
	| operator_ = unaryOperator argument_ = expression {
        //NOTE @Add unaryExpression following estree
        $type = "UnaryExpression";
    } # UnaryExpression
	| left_ = expression operator_ = exponentiationOperator right_ = expression {
        //NOTE @Add Exponentiation operator following ES2016
        $type = "BinaryExpression";
    } # ExponentiationExpression
	| left_ = expression operator_ = multiplicativeOperator right_ = expression {
        $type = "BinaryExpression";
    } # MultiplicativeExpression
	| left_ = expression operator_ = additiveOperator right_ = expression {
        $type = "BinaryExpression";
    } # AdditiveExpression
	| left_ = expression operator_ = bitShiftOperator right_ = expression {
        $type = "BinaryExpression";
    } # BitShiftExpression
	| left_ = expression operator_ = relationalOperator right_ = expression {
        $type = "BinaryExpression";
    } # RelationalExpression
	| left_ = expression operator_ = instanceofOperator right_ = expression {
        $type = "BinaryExpression";
    } # InstanceofExpression
	| left_ = expression operator_ = inOperator right_ = expression {
        $type = "BinaryExpression";
    } # InExpression
	| left_ = expression operator_ = equalityOperator right_ = expression {
        $type = "BinaryExpression";
    } # EqualityExpression
	| left_ = expression operator_ = bitAndOperator right_ = expression {
        $type = "BinaryExpression";
	} # BitAndExpression
	| left_ = expression operator_ = bitXorOperator right_ = expression {
        $type = "BinaryExpression";
	} # BitXorExpression
	| left_ = expression operator_ = bitOrOperator right_ = expression {
        $type = "BinaryExpression";
	} # BitOrExpression
	| left_ = expression '&&' right_ = expression {
        $type = "LogicalExpression";
		this->validateNullishOperatorMixedWithOtherLogicalOp($ctx);
	} # LogicalExpression
	| left_ = expression '||' right_ = expression {
        $type = "LogicalExpression";
		this->validateNullishOperatorMixedWithOtherLogicalOp($ctx);
		} # LogicalExpression
	| left_ = expression '??' right_ = expression {
        $type = "LogicalExpression";
		this->validateNullishOperatorMixedWithOtherLogicalOp($ctx);
	} # LogicalExpression
	| test_ = expression '?' consequent_ = expression ':' alternate_ = expression {
        $type = "ConditionalExpression";
	    } # ConditionalExpression
	| test_ = expression QuestionMarkFollowedByDecimalDirectly ':' alternate_ = expression {
        $type = "ConditionalExpression";
	} # QuestionMarkFollowedByDecimalDirectlyConditionExpression
	| left_ = pattern operator_ = assignmentOperator right_ = expression {
        $type = "AssignmentExpression"; 
    } # PatternInvolvedAssignmentExpression
	| left_ = expression operator_ = assignmentOperator right_ = expression {
        $type = "AssignmentExpression";

    } # AssignmentExpression
	| templateLiteral {
        $type ="TemplateLiteral";
    } # TemplateLiteralExpression
	| tag_ = expression quasi_ = templateLiteral {
        $type = "TaggedTemplateExpression";
    } # TaggedTemplateExpression
	| iteratorBlock {
        $type = "IteratorsExpression";
    } # IteratorsExpression // ECMAScript 6
	| generatorBlock {
        $type = "GeneratorsExpression";
    } # GeneratorsExpression // ECMAScript 6
	| generatorFunctionDeclaration {
        $type = "GeneratorsFunctionExpression";
    } # GeneratorsFunctionExpression // ECMAScript 6
	| Yield '*'? argument_ = expression {
        $type = "YieldExpression";
    } # YieldExpression // ECMAScript 6
	| Await argument_ = expression {
        $type = "AwaitExpression";
    } # AwaitExpression
	| Import '(' source_ = expression ')' {
        $type = "ImportExpression";
    } # ImportExpression
	| meta_ = metaPropertyIdentifier '.' property_ = identifier {
        $type = "MetaProperty";
    } # MetaProperty
	| This {
        $type = "ThisExpression";
    } # ThisExpression
	| identifier {
        $type = "Identifier";
    } # IdentifierExpression
	| Super {
        $type = "Super"; 
    } # SuperExpression
	| literal {
        $type = "Literal";
    } # LiteralExpression
	| templateLiteral {
        $type = "TemplateLiteral";
    } # TemplateLiteralExpression
	| '[' elements_ = arrayElementArrayList? ']' {
        $type = "ArrayExpression";
    } # ArrayExpression
	| '{' properties_ = objectElementArrayList? '}' {
        $type = "ObjectExpression";
    } # ObjectExpression
	| expression_ = expression As (
		typeAnnotation_ = type_ ('[' ']')?
		| asExpression_ = expression
	) {
        //TODO refer to parser result from https://astexplorer.net/ @typescript-eslint/parser
        $type = "TSAsExpression";
    } # TSAsExpression;

identifier
	locals[ CppString type , CppString name ]:
	Identifier (
		{this->canBeFollowedByTypeAnnotation()}? typeAnnotation_ = typeAnnotation
	)? {
        $type = "Identifier";
        $name =  $ctx->getText(); 
    };
metaPropertyIdentifier
	locals[CppString type , CppString name]:
	New {
        $type = "Identifier";
        $name = "new";
    }
	| Import {
        $type = "Identifier";
        $name = "Import";
    };

//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
privateIdentifier
	locals[ CppString type , CppString name ]:
	'#' Identifier {
        $type = "PrivateIdentifier";
        $name =  $Identifier.text;
    };

//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
memberExpressionProperty
	locals[ CppString type , boolean computed , boolean optional ]:
	'[' expression_ = expressionSequence ']' { 
        $computed = true; 
        $optional = false;
        $type = "BracketMemberExpression";
    } # BracketMemberExpression
	| OptionalChainingOperator '[' expression_ = expressionSequence ']' { 
		$computed = true; 
        $optional = true;
	        $type = "BracketMemberExpression";
	} # BracketMemberExpression
	| OptionalChainingOperator identifier { 
        $computed = false; 
        $optional = true;
        $type = "DotMemberExpression";
    } # DotMemberExpression
	| '.' identifier { 
        $computed = false; 
        $optional = false;
        $type = "DotMemberExpression"; 
    } # DotMemberExpression
	| '.' privateIdentifier { 
        $computed = false; 
        $optional = false;
        $type = "PrivateMemberExpression"; 
    } # PrivateMemberExpression;

arrowFunctionParameters: '(' parameterList? ')' | identifier;

arrowFunctionBody: braceBlock | expression;

//NOTE @Merge merge '=' here for compatibility with estree
assignmentOperator:
	'='
	| '*='
	| '/='
	| '%='
	| '**=' //NOTE @Add add '**=' following ES2016
	| '+='
	| '-='
	| '<<='
	| '>>='
	| '>>>='
	| '&='
	| '^='
	| '|=';

numericLiteral:
	DecimalLiteral
	| HexIntegerLiteral
	| OctalIntegerLiteral
	| OctalIntegerLiteral2
	| BinaryIntegerLiteral;

literal
	locals[ CppString type ]
	@init {
    $type = "Literal";
}:
	NullLiteral
	| BooleanLiteral
	| StringLiteral
	| RegularExpressionLiteral {
		this->checkIsValidRegexFlag($ctx->getText());
	}
	| numericLiteral
	| BigInt;

templateLiteral
	locals[ CppString type ]
	@init {
    $type = "TemplateLiteral";
}: BackTick templateStringAtom_ = templateStringAtom* BackTick;

templateStringAtom:
	TemplateStringAtom															# AtomTemplateString
	| TemplateStringStartExpression expression_ = expression TemplateCloseBrace	# BraceTemplateString
		;

//NOTE require is treated  as noraml identifier
keyword:
	Break
	| Do
	| Instanceof
	| Typeof
	| Case
	| Else
	| New
	| Var
	| Catch
	| Finally
	| Return
	| Void
	| Continue
	| For
	| Switch
	| While
	| Debugger
	| Function_
	| This
	| With
	| Default
	| If
	| Throw
	| Delete
	| In
	| Of
	| Try
	| ReadOnly
	| Async
	| From
	| Class
	| Enum
	| Extends
	| Super
	| Const
	| Export
	| Import
	| Implements
	| Let
	| Private
	| Public
	| Interface
	| Package
	| Protected
	| Static
	| Yield
	| Get
	| Set
	| TypeAlias
	| String;

getter: Get propertyName_ = propertyName;

setter: Set propertyName_ = propertyName;

eos:
	SemiColon
	| EOF
	| {this->lineTerminatorAhead()}?
	| {this->closeBrace()}?;

assignable: pattern | expression;
//Type annotation ,reference to @typescript-eslint/parser 

typeAnnotation
	locals[ CppString type ]:
	':' typeAnnotation_ = type_ {
         $type = "TypeAnnotation";
    };

qualifiedName
	locals[CppString type]
	@after {
			$type = "QualifiedName";

	}:
	(identifier '.')+ identifier
	| (identifier '.')+ {
		this->insertDummyNode();
	}
	| This ('.' identifier)+
	| This ('.' identifier)* '.' {
		this->insertDummyNode();
	};

typeName: identifier | qualifiedName;

//https://developer.mozilla.org/en-US/docs/Glossary/Primitive
jsPrimitiveType:
	Boolean
	| Number
	| String
	| NullLiteral
	| Symbol
	| Undefined
	| BigIntAnnotation;

//More precious primitive types 
extendedPrimitiveType:
	Int8
	| Int16
	| Int32
	| Int64
	| Uint8
	| Uint16
	| Uint32
	| Uint64
	| Float
	| Double
	| Decimal;

typeList
	locals[ boolean isArrayList  ]:
	type_ (',' type_)* {
			$isArrayList = true;
	}
	| type_ (',' type_)* ',' {
		this->insertDummyNode();
	};

anonymousObjectPropertyListSeparator: ',' | ';';

// https://www.typescriptlang.org/docs/handbook/2/objects.html
anonymousObjectPropertyList
	locals[ boolean isArrayList  ]:
	objectPropertyPair (
		anonymousObjectPropertyListSeparator objectPropertyPair
	)* {
					$isArrayList = true;
	};

objectPropertyPair:
	key = identifier ':' value = type_
	| key = identifier ':' {
		this->insertDummyNode();
	};

genericKind: Array | Object;

commaGeneric
	locals[ CppString type ]:
	t0 = genericKind ',' internalType = type_ {
	$type = $t0.text;
}
	| t0 = genericKind ',' {
	this->insertDummyNode();
	$type = $t0.text;
};

angleGeneric
	locals[ CppString type ]
	@after {
	$type = $t0.text;
}:
	t0 = genericKind '<' internalType = type_ '>'
	| t0 = genericKind '<' t1 = genericKind '<' internalType = type_ (
		('>' '>')
		| '>>'
	)
	| t0 = genericKind '<' t1 = genericKind '<' t2 = genericKind '<' internalType = type_ (
		('>' '>' '>')
		| '>>>'
	)
	| t0 = genericKind '<' internalType = type_ ('>')* {
		this->insertDummyNode();
	}
	| t0 = genericKind '<' t1 = genericKind '<' internalType = type_ (
		('>')*
		| '>>'
	) {
		this->insertDummyNode();
	}
	| t0 = genericKind '<' t1 = genericKind '<' t2 = genericKind '<' internalType = type_ (
		('>')*
		| '>>>'
	) {
		this->insertDummyNode();
	};

objectOrArrayGenericType: commaGeneric | angleGeneric;

type_
	locals[ CppString type  ]:
	jsPrimitiveType {
		$type = _localctx->getText();
	} # PrimitiveType
	| extendedPrimitiveType {
		$type = _localctx->getText();
	} # PrimitiveType
	| Any {
		$type = _localctx->getText();
	} # PrimitiveType
	| objectOrArrayGenericType {
		$type = "genericType";
	} # GenericType
	| '[' elements = typeList ']' {
		$type = "tuple";
	} # TupleType
	| '[' ']' {
		$type = "tuple";
		this->insertDummyNode();

		} # TupleType
	| '[' {
		$type = "tuple";
		this->insertDummyNode();
	} # TupleType
	| type_ ('|' type_)+ {
		$type = "union";
	} # UnionType
	| type_ ('|' type_)+ '|' {
		$type = "union";
		this->insertDummyNode();
	} # UnionType
	| '{' properties = anonymousObjectPropertyList? '}'? {
		$type = "struct";
	} # Struct
	| {this->allowParenthesesInType()}? '(' internalType = type_ ')' {
		$type = "parenthesizedPrimType";
	} # ParenthesizedPrimType
	| {this->allowParenthesesInType()}? '(' ')'? {
		$type = "parenthesizedPrimType";
		this->insertDummyNode();
	} # ParenthesizedPrimType
	| typeName_ = typeName {
		$type = "typeReference";
	} # TypeReference;

typeAnnot: type_ eos;

//https://www.typescriptlang.org/docs/handbook/advanced-types.html
typePredicate:
	parameterName_ = identifier Is typeAnnotation_ = type_;

functionReturnType: type_ | Void | typePredicate;

